/**
  ******************************************************************************
  * @file           : func.c
  * @brief          : bootloader function code
  * @author         : Qilong Huang <jcdiyhelp@qq.com>
  * @date           : 2021-06-08 
  * 
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2021 Joy create tech(Suzhou, China) Co., Ltd.
  * All rights reserved.
  *
  *
  ******************************************************************************
  */
/* Global include ---------------------------------------------------------*/
#include "func.h"
#include "string.h"
/* Private function declaration*/
static void FlashHalfPageClean(TYP_FlashHalfPage_t *p);

void FUNC_UsartMessageHander(UINT8_t *Data_in, UINT8_t Length);
void FUNC_FrameFormat(UINT8_t *Data_in, TYP_SingalFrame_t *Data_out);
UINT8_t FUNC_FrameReFormat(UINT8_t *Data_out, TYP_SingalFrame_t *Data_in);
void FUNC_ResponseSend(TYP_SPResp_t RespCode);
FLASH_OBProgramInitTypeDef OBInit;

/* Private variable definition*/
static volatile TYP_BOOL UsartLoopFLag;
/**
 * @brief   Actually jumps to the user application.
 * @param   void
 * @return  void
 */
void FUNC_JampToApp(void)
{
    TYP_drcPtr jump2app;
    __set_PRIMASK(1);
    __disable_irq();
    jump2app = (TYP_drcPtr)(*(volatile uint32_t *)(AppSWAddress + 4u));
    HAL_DeInit();            //Note: For happy to go, this code must be run!!
    __set_MSP( * ( __IO uint32_t * )AppSWAddress); //Remap the stack start address
    for(int i = 0; i < 8; i++) {
        NVIC->ICER[i] = 0xFFFFFFFF; // 关闭中断
        NVIC->ICPR[i] = 0xFFFFFFFF; // 清除中断标志位
    }
    HAL_RCC_DeInit();
    // DeInit();
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    jump2app();
}

/**
 * @brief   This function is executed in case of error occurrence.
 * @param   void
 * @return  void
 */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
    /* USER CODE END Error_Handler_Debug */
}

/**
 * @brief   This function initial the hardware.
 * @param   void
 * @return  void
 */
void FUNC_HardwareInit(void)
{
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    USART_uartInit();
}

/**
 * @brief   This function initial the hardware.
 * @param   void
 * @return  void
 */
void FUNC_WaitProgram(void)
{
    UsartLoopFLag = TYP_TRUE;
    while (UsartLoopFLag == TYP_TRUE)
    {
        if (RequireBCFlashFlag == false)
        {
            if (UsartBuffer.BufferHead != UsartBuffer.BufferTail)
            {
                FUNC_UsartMessageHander(UsartBuffer.BufferBody[UsartBuffer.BufferTail].data, MAX_USART_LEN);
                UsartBuffer.BufferTail++;
                UsartBuffer.BufferTail &= (MAX_USART_BUF - 1);
            }
        }
        else
        {
            FUNC_StartProgram(); //Start to program
        }
    }
}

/**
 * @brief   This function is to handle the USART singal frame.
 * @param   Data_in  Sighal 22 bytes data input
 * @param   Length   Reserve
 * @return  void
 */
TYP_SingalFrame_t Message;
void FUNC_UsartMessageHander(UINT8_t *Data_in, UINT8_t Length)
{
    TYP_SingalFrame_t Message;
    if (!FUNC_CrcCheck(Data_in, Length))
    {
        FUNC_FrameFormat(Data_in, &Message);
        if (Message.Length >= 10) //To prevent all 0 in frame
        {
            switch (Message.Cmd.Cmd_Str.OpCode)
            {
            /*Common read*/
            case 0b00:
                FUNC_CommonReadRegister(&Message);
                break;
            /*Common write*/
            case 0b10:
                FUNC_CommonWriteRegister(&Message);
                break;
            case 0b11:
                FUNC_SecretWriteRegister(&Message);
                break;
            default:
                break;
            }
        }
    }
    else
    {
        FUNC_ResponseSend(RESP_CRC_ERR);
    }
}

/**
 * @brief   This function is to handle the USART singal frame.
 * @param   Data_in  Sighal 22 bytes data input
 * @param   Data_out Signal frame output after formatting.
 * @return  void
 */
void FUNC_FrameFormat(UINT8_t *Data_in, TYP_SingalFrame_t *Data_out)
{
    int i;
    for (i = 0; i < 2; i++)
    {
        Data_out->StartByte[i] = Data_in[i]; //Copy frame start flag
    }
    Data_out->Length = Data_in[2];
    Data_out->Cmd.Cmd_U8 = Data_in[3];
    for (i = 0; i < 4; i++)
    {
        Data_out->Addr.Address_U8[i] = Data_in[i + 4];
    }
    for (i = 0; i < 16; i++)
    {
        Data_out->Data[i] = Data_in[i + 8];
    }
    for (i = 0; i < 2; i++)
    {
        Data_out->Crc[i] = Data_in[i + 24];
    }
}

/**
 * @brief   This function is to trans singal frame to USART array.
 * @param   Data_in  Sighal 22 bytes data input
 * @param   Data_out Signal frame output after formatting.
 * @return  void
 */
UINT8_t FUNC_FrameReFormat(UINT8_t *Data_out, TYP_SingalFrame_t *Data_in)
{
    int i;
    UINT16_t CRC_Val;
    UINT8_t RetVal = MAX_USART_LEN;
    Data_out[0] = MESSID_LSB;
    Data_out[1] = MESSID_MSB;
    Data_out[2] = MAX_USART_LEN;
    Data_out[3] = Data_in->Cmd.Cmd_U8;
    Data_out[4] = Data_in->Addr.Address_U8[0];
    Data_out[5] = Data_in->Addr.Address_U8[1];
    Data_out[6] = Data_in->Addr.Address_U8[2];
    Data_out[7] = Data_in->Addr.Address_U8[3];
    for (i = 0; i < 16; i++)
    {
        Data_out[8 + i] = Data_in->Data[i];
    }
    CRC_Val = FUNC_CrcCheck(Data_out, MAX_USART_LEN - 2);
    Data_out[24] = CRC_Val;
    Data_out[24] = CRC_Val >> 8;
    return RetVal;
}

/**
 * @brief   This function is calculate crc.
 * @param   data   Data array pointer
 * @param   length Data array length.
 * @return  void
 */
UINT16_t FUNC_CrcCheck(UINT8_t *data, UINT8_t length)
{
    UINT8_t i, j, bLsb, BArrayData;
    UINT16_t CrcReg = 0xffff;
    for (i = 0; i < length; i++)
    {
        BArrayData = *data;
        CrcReg = ((BArrayData ^ CrcReg) & 0x00ff) | (CrcReg & 0xff00);
        for (j = 0; j < 8; j++)
        {
            bLsb = CrcReg & 0x0001;
            CrcReg = (CrcReg >> 1) & 0x7fff;
            if (bLsb == 0x0001)
            {
                CrcReg = CrcReg ^ 0xA001;
            }
        }
        data++;
    }
    return CrcReg;
}

/**
 * @brief   This function to deal with common read.
 * @param   Msg   Singal data frame after formatting
 * @return  void
 */
void FUNC_CommonReadRegister(TYP_SingalFrame_t *Msg)
{
    UINT8_t RespData[26];
    //Msg->Data[0] = 0xFF;
    memcpy(Msg->Data,
           InfoTable[Msg->Addr.Address_U32 & 0x000F].VarAddress,
           InfoTable[Msg->Addr.Address_U32 & 0x000F].SpaceLen);
    FUNC_FrameReFormat(RespData, Msg);
    HAL_UART_Transmit(&huart1, (uint8_t *)RespData, MAX_USART_LEN, 100);
}

/**
 * @brief   This function to deal with common write.
 * @param   Msg   Singal data frame after formatting
 * @return  void
 */
void FUNC_CommonWriteRegister(TYP_SingalFrame_t *Msg)
{
    UINT8_t RespData[26];
    //Msg->Data[0] = 0xFF;
    memcpy(InfoTable[Msg->Addr.Address_U32 & 0x000F].VarAddress,
           Msg->Data,
           InfoTable[Msg->Addr.Address_U32 & 0x000F].SpaceLen);
    FUNC_FrameReFormat(RespData, Msg);
    HAL_UART_Transmit(&huart1, (uint8_t *)RespData, MAX_USART_LEN, 100);
}

/*
 *
 */
void FUNC_SendStartTransInfo(void)
{
    int i = 0;
    UINT8_t RespData[26];
    TYP_SingalFrame_t TransInfo;
    TransInfo.StartByte[0] = 0x55;
    TransInfo.StartByte[1] = 0xAA;
    TransInfo.Length = 26;
    TransInfo.Cmd.Cmd_U8 = 0xFF;
    TransInfo.Addr.Address_U32 = 0xFF;
    for (i = 0; i < 16; i++)
    {
        TransInfo.Data[i] = 0xFF;
    }
    FUNC_FrameReFormat(RespData, &TransInfo);
    HAL_UART_Transmit(&huart1, (uint8_t *)RespData, MAX_USART_LEN, 100);
}

/**
 * @brief   This function to flash programming.
 * @param   none
 * @return  void
 */
void FUNC_StartProgram(void)
{
    USART_ClearQueue(); //Clean all the data of USART1 receive buffer queue
    
    FUNC_HardwareInit();

    FLASH_EraseAppArea(AppSWAddress, AppSWAddress + AppMaxLen); //Erase appication sw flash area

    HAL_FLASHEx_OBGetConfig(&OBInit);
    FUNC_SendStartTransInfo();
    FlashHalfPageClean(&FlashHalfBuffer);


    while (1)
    {
        if (UsartBuffer.BufferHead != UsartBuffer.BufferTail)
        {
            FUNC_UsartMessageHander(UsartBuffer.BufferBody[UsartBuffer.BufferTail].data, MAX_USART_LEN);
            UsartBuffer.BufferTail++;
            UsartBuffer.BufferTail &= (MAX_USART_BUF - 1);
            if(BCFlashFinishedFlag == true)
            {
                AppEmptyFlag = X_AREA_FULL;
                I_EEPROM_WriteBuff(InfoTable[AppEmptyFlagIndex].EepStartAddress, (UINT8_t *)&AppEmptyFlag, InfoTable[AppEmptyFlagIndex].SpaceLen);
                __set_FAULTMASK(1); 
                HAL_NVIC_SystemReset();
            }
        }
    }
}

/**
 * @brief   This function to deal with special write.
 * @param   Msg   Singal data frame after formatting
 * @return  void
 */
void FUNC_SecretWriteRegister(TYP_SingalFrame_t *Msg)
{
    UINT8_t RespData[26], i;
    UINT32_t Address, data = 0x00000000UL;
    Address = Msg->Addr.Address_U32;
    TYP_funcRetrun_t RetuenMeg;

    if (RequireHalfProgram == TYP_FALSE)
    {
        for (i = 0; i < WORD_LEN; i++)
        {
            data <<= 8;
            data |= Msg->Data[3 - i]; //Little endian
        }
        RetuenMeg = FLASH_WriteAWord(Address, data);
        if(RetuenMeg != TYP_SUCCESS)
        {
            FUNC_ResponseSend(RESP_FLASH_ERR);
        }
        else
        {
            FUNC_ResponseSend(RESP_OK);
        }
    }
    else
    {
        for (i = 0; i < 4; i++)
        {
            FlashHalfBuffer.FlashData[FlashHalfBuffer.FlashDataPt++] = (Msg->Data[3 + i * 4] << 24) |
                                                                       (Msg->Data[2 + i * 4] << 16) |
                                                                       (Msg->Data[1 + i * 4] << 8) |
                                                                       (Msg->Data[0 + i * 4]);
            FlashHalfBuffer.FlashFrameNumber++;
        }
        if (FlashHalfBuffer.FlashFrameNumber >= FLASH_HALFPAGE_LEN)
        {
            RetuenMeg = FLASH_WriteHalfPage(Address, FlashHalfBuffer.FlashData);
            //FUNC_FrameReFormat(RespData, Msg);
            //HAL_UART_Transmit(&huart1, (uint8_t *)RespData, MAX_USART_LEN, 100);
            FlashHalfPageClean(&FlashHalfBuffer);
        }
        FUNC_ResponseSend(RESP_OK);
    }
}

static void FlashHalfPageClean(TYP_FlashHalfPage_t *p)
{
    int i = 0;
    for (i = 0; i < FLASH_HALFPAGE_LEN; i++)
    {
        p->FlashData[i] = 0x00;
    }
    p->FlashDataPt = 0x00;
    p->FlashFrameNumber = 0x00;
}

void FUNC_ResponseSend(TYP_SPResp_t RespCode)
{
    TYP_SingalFrame_t RespMsg;
    UINT8_t RespData[26];
    RespMsg.StartByte[0] = 0x55;
    RespMsg.StartByte[1] = 0xAA;
    RespMsg.Length = 26;
    RespMsg.Cmd.Cmd_U8 = 0xFF;
    RespMsg.Addr.Address_U32 = 0xFFFFFFFFUL;
    RespMsg.Data[0] = RespCode;
    RespMsg.Data[1] = RespCode;
    FUNC_FrameReFormat(RespData, &RespMsg);
    HAL_UART_Transmit(&huart1, (uint8_t *)RespData, MAX_USART_LEN, 100);
}

void FUNC_AppMove(void)
{
    UINT32_t TempAddr;
    UINT32_t FlashData[32];
    FLASH_EraseAppArea(AppSWAddress, AppSWAddress + AppMaxLen); //Erase appication sw flash area
    for (TempAddr = AppSWAddress; TempAddr < AppSWAddress + BackMaxLen; TempAddr += FLASH_HALF_PAGE_SIZE)
    {
        FLASH_ReadHalfPage(TempAddr + 0x6000, FlashData);
        FLASH_WriteHalfPage(TempAddr, FlashData);
    }
}